package info.motteke.mybws.utils.test;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

/**
 * 要素の数をチェックする{@linkplain Matcher}の実装です。
 * 以下の実装に対して有効です。
 * 
 * <ul>
 * <li>{@linkplain CharSequence}
 * <li>{@linkplain Collection}
 * <li>配列
 * </ul>
 * @author YWak
 *
 * @param <T>
 */
public class SameSizeMatcher<T> extends BaseMatcher<T> {

    private final T expected;

    public SameSizeMatcher(T expected) {
        if (expected == null) {
            throw new NullPointerException("expected can't be null.");
        }
        
        if (getSize(expected) == -1) {
            throw new IllegalArgumentException(expected.getClass().getName() + " has no size");
        }

        this.expected = expected;
    }

    @Override
    public boolean matches(Object item) {
        return getSize(item) == getSize(expected);
    }

    @SuppressWarnings("rawtypes")
    private int getSize(Object item) {
        if (item instanceof CharSequence) {
            return ((CharSequence)item).length();
        }

        if (item instanceof Collection) {
            return ((Collection)item).size();
        }
        
        if (item instanceof Map) {
            return ((Map)item).size();
        }

        if (item.getClass().isArray()) {
            return Array.getLength(item);
        }

        return -1;
    }

    @Override
    public void describeTo(Description description) {
        description.appendValue(getSize(expected));
    }

    @Override
    public void describeMismatch(Object item, Description description) {
        description.appendText("was ").appendValue(getSize(item));
    }

}
